/*
 * Javascript Agile Suite - JasMock Library
 * http://code.google.com/p/jasproject/
 *
 * Copyright (C) 2009 Luke Schafer
 * Licensed under MIT X11 license:
 * http://www.opensource.org/licenses/mit-license.php
 */

var Assert = (function()
{
	_assert.prototype.constructor = _assert;
	function _assert() { }

	_assert.prototype.isTrue = function(input)
	{
		if (!input) throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.ASSERTIONEXCEPTION,
			"Expression or value was not true"
		));
	}
	
	_assert.prototype.isFalse = function(input)
	{
		if (input) throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.ASSERTIONEXCEPTION,
			"Expression or value was not false"
		));
	}

	_assert.prototype.isNull = function(input)
	{
		if (input !== null) throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.ASSERTIONEXCEPTION,
			"Object is not null"
		));
	}
	
	_assert.prototype.isNotNull = function(input)
	{
		if (input === null) throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.ASSERTIONEXCEPTION,
			"Object is null"
		));
	}

	_assert.prototype.isNaN = function(input)
	{
		if (!isNaN(input)) throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.ASSERTIONEXCEPTION,
			"Object is not a number"
		));
	}
	
	_assert.prototype.isNotNaN = function(input)
	{
		if (isNaN(input)) throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.ASSERTIONEXCEPTION,
			"Object is a number"
		));
	}
	
	_assert.prototype.isUndefined = function(input)
	{
		if (value !== undefined) throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.ASSERTIONEXCEPTION,
			"Object is not defined"
		));
	}
	
	_assert.prototype.isNotUndefined = function(input)
	{
		if (value === undefined) throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.ASSERTIONEXCEPTION,
			"Object is defined"
		));
	}
		
	_assert.prototype.areEqual = function(expected, value)
	{
		if (value !== expected) throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.ASSERTIONEXCEPTION,
			Jas.stringFormat("Value does not match expected. Expected: {0}, Actual: {1}", expected, value)
		));
	}
	
	_assert.prototype.areNotEqual = function(notexpected, value)
	{
		if (value === notexpected) throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.ASSERTIONEXCEPTION,
			Jas.stringFormat("Value matches unexpected value. Value should not equal: {0}", notexpected)
		));
	}
		
	_assert.prototype.contains = function(arr, value)
	{
		for (var i = 0; i < arr.length; i++)
		{
			if (arr[i] === value) return;
		}
		throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.ASSERTIONEXCEPTION,
			Jas.stringFormat("Array does not contain the given value")
		));
	}

	_assert.prototype.notContains = function(arr, value)
	{
		for (var i = 0; i < arr.length; i++)
		{
			if (arr[i] === value) throw(Jas.Exceptions.create(
				Jas.Exceptions.Types.ASSERTIONEXCEPTION,
				Jas.stringFormat("Array contains a value it should not")
			));
		}
	}

	_assert.prototype.fail = function()
	{
		if (value !== undefined) throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.ASSERTIONEXCEPTION,
			"Test Failed"
		));
	}

	_assert.prototype.ignore = function()
	{
		throw(Jas.Exceptions.create(
			Jas.Exceptions.Types.IGNORETESTEXCEPTION,
			"Test Ignored"
		));
	}

	return new _assert();
})();

var JasUnit = (function()
{
	_jasUnit.prototype.constructor = _jasUnit;
	function _jasUnit()
	{
		this.logger = {
			log : function(status, message, testName, namespace)
			{
				var result = Jas.stringFormat(
					"[{0}] {1}{2} : {3}<br />", 
					status == 2 ? "IGNORE" : status == 1 ? "PASSED" : "FAILED", 
					namespace ? namespace + "." : "", 
					testName,
					message
				);
				document.write(result); // should change this - browser-centric
			}
		}

		this.assertScope = {};
		for (var k in Assert)
		{
			this.assertScope["assert" + k.replace(/^(.)/, function(a){return a.toUpperCase();}) ] = Assert[k]
		}
	}

	_jasUnit.prototype.setup = function(config)
	{
		this.logger = config.logger || this.logger;
		this.ignores = config.ignores || [];
		
		if (config.pushAssertionsToGlobal)
		{
			for (var i in this.assertScope)
			{
				if (this.assertScope.hasOwnProperty(i))
					eval(i + " = this.assertScope." + i); //eval rather than window[i] keeps it platform agnostic.
			}
		}
	}

	_jasUnit.prototype.runTests = function(fixture)
	{
		for (var i in fixture)
		{
			if (i.match(/^[Tt]est/))
			{
				try
				{
					for (var j = 0; j < this.ignores.length; j++)
					{
						if (this.ignores[j] == i) Assert.ignore();
					}

					fixture.setup && fixture.setup();

					fixture[i]();
					this.logger.log(1, "Test Succeeded", i, fixture.namespace);
				}
				catch (ex)
				{
					if (ex.type == Jas.Exceptions.Types.IGNORETESTEXCEPTION)
						this.logger.log(2, "Test Ignored", i, fixture.namespace);
					else
						this.logger.log(0, ex, i, fixture.namespace);
				}
				fixture.teardown && fixture.teardown();
			}
		}
		if (fixture.subFixtures && fixture.subFixtures.length > 0)
		{
			for (var j = 0; j < fixture.subFixtures.length; j++)
			{
				this.runTests(fixture.subFixtures[j]);
			}
		}
	}

	_jasUnit.prototype.Events = {
		simulate : function(elem, eventName, config)
		{
			var evt;
			function getVal(val, otherwise)
			{ return (val !== null && val !== undefined) ? val : otherwise; }

			function bindEventParams(evt, ignoreBaseProperties)
			{
				evt.screenX = getVal(config.screenX, 0);
				evt.screenY = getVal(config.screenY, 0);
				evt.clientX = getVal(config.clientX, 0);
				evt.clientY = getVal(config.clientY, 0);
				evt.ctrlKey = getVal(config.ctrlKey, false);
				evt.altKey = getVal(config.altKey, false);
				evt.shiftKey = getVal(config.shiftKey, false);
				evt.metaKey = getVal(config.metaKey, false);
				evt.button = getVal(config.button, 0);
				evt.relatedTarget = getVal(config.relatedTarget, null);
				evt.keyCode = getVal(config.keyCode, 13);
				evt.charCode = getVal(config.charCode, 'a');
				evt.keyCode = 13;
			}

			if (document.createEvent)
			{ // DOM2 Compatible
				if (eventName == "click" || eventName.match(/^mouse/))
				{
					evt = document.createEvent('MouseEvents');
					evt.initMouseEvent(
						eventName,
						getVal(config.bubbles, true), 
						getVal(config.cancelable, true), 
						getVal(config.windowObject, window), 
						getVal(config.detail, 0), 
						getVal(config.screenX, 1), 
						getVal(config.screenY, 1), 
						getVal(config.clientX, 1), 
						getVal(config.clientY, 220), 
						getVal(config.ctrlKey, false), 
						getVal(config.altKey, false), 
						getVal(config.shiftKey, false), 
						getVal(config.metaKey, false), 
						getVal(config.button, 0), 
						getVal(config.relatedTarget, null)
					);
				}
				else if (eventName.match(/^key/))
				{
					// Webkit: Safari/Chrome - don't allow UIEvents to have properties bound, so need a
					// generic event (or UIEvents for safari 2.x). This is a nasty hack.
					try
					{
						// Fail in Safari 2.x
						evt = document.createEvent("Events");
					}
					catch (err)
					{
						evt = document.createEvent("UIEvents");
					}

					evt.initEvent(
						eventName,
						getVal(config.bubbles, true),
						getVal(config.cancelable, true),
						getVal(config.windowObject, window),
						getVal(config.detail, 1)
					);
					bindEventParams(evt, true);
				}
				else
				{
					evt = document.createEvent('HTMLEvents');
					evt.initEvent(eventName, getVal(config.bubbles, true), getVal(config.cancelable, true));
				}
				elem.dispatchEvent(evt);
			}
			else if (document.createEventObject)
			{
				// IE
				evt = document.createEventObject();
				bindEventParams(evt);
				evt.detail = getVal(config.detail, 0);
				elem.fireEvent('on' + eventName, evt);
			}
		},

		// Mouse
		click : function(elem, config) { this.simulate(elem, "click", config); },
		mousedown : function(elem, config) { this.simulate(elem, "mousedown", config); },
		mousemove : function(elem, config) { this.simulate(elem, "mousemove", config); },
		mouseout : function(elem, config) { this.simulate(elem, "mouseout", config); },
		mouseover : function(elem, config) { this.simulate(elem, "mouseover", config); },
		mouseup : function(elem, config) { this.simulate(elem, "mouseup", config); },
		
		// Key
		keydown : function(elem, config) { this.simulate(elem, "keydown", config); },
		keyup : function(elem, config) { this.simulate(elem, "keyup", config); },
		keypress : function(elem, config) { this.simulate(elem, "keypress", config); },

		// Other
		blur : function(elem, config) { this.simulate(elem, "blur ", config); },
		focus : function(elem, config) { this.simulate(elem, "focus", config); },
		change : function(elem, config) { this.simulate(elem, "change", config); },
		resize : function(elem, config) { this.simulate(elem, "resize", config); },
		load : function(elem, config) { this.simulate(elem, "load", config); },
		unload : function(elem, config) { this.simulate(elem, "unload", config); },
		select : function(elem, config) { this.simulate(elem, "select", config); },
		submit : function(elem, config) { this.simulate(elem, "submit", config); }
	}

	return new _jasUnit();
})();

JasUnit.DefaultLogger = (function()
{
	_defaultLogger.prototype.constructor = _defaultLogger;
	function _defaultLogger(el)
	{
		this.el = (typeof el == "string") ? document.getElementById(el) : el;
		this.table = document.createElement("table");
		this.el.appendChild(this.table);
		this.tableBody = document.createElement("tbody");
		this.table.appendChild(this.tableBody);
	}

	_defaultLogger.prototype.log = function(status, message, testName, namespace)
	{
		var row, td
			
		row = document.createElement( "tr" );
		row.style.backgroundColor = status == 2 ? "yellow" : status == 1 ? "green" : "red";
		row.style.color = status == 2 ? "black" : status == 1 ? "white" : "black";

		function append(text)
		{
			td = document.createElement( "td" );
			td.style.fontFamily = "courier new";
			td.style.fontSize = "10pt";
			td.innerHTML = text;
			row.appendChild(td);
		}

		append(status == 2 ? "IGNORE" : status == 1 ? "PASSED" : "FAILED");
		append(Jas.stringFormat("{0}{1}", namespace ? namespace + "." : "", testName));
		append(message);

		this.tableBody.appendChild(row);
	}

	return _defaultLogger;
})();